#pragma once

#include "../../engine/module_define.hh"
#include "../../engine/protocol_analyzer.hh"
#include "../../engine/service.hh"
#include "../../engine/engine.hh"
#include "../../engine/component.hh"
#include "../../corelib/singleton.hpp"
#include "../../corelib/logger/log.hh"
#include "../../corelib/util/allocator.hh"
#include <functional>
#include <memory>

#ifdef KRATOS_COMPILER_LOG_OFF
#define FRAMEWORK_CHECK_RETURN(expr, result, log)\
    if (expr) {\
        return result;\
    }
#define FRAMEWORK_CHECK(expr, log)\
    if (expr) {\
        return;\
    }
#else
#define FRAMEWORK_CHECK_RETURN(expr, result, log)\
    if (expr) {\
        errorlog << log << endlog;\
        return result;\
    }
#define FRAMEWORK_CHECK(expr, log)\
    if (expr) {\
        errorlog << log << endlog;\
        return;\
    }
#endif // #ifdef KRATOS_COMPILER_LOG_OFF

using namespace kratos::engine;
using namespace kratos::corelib;

namespace flatbuffers {
    class FlatBufferBuilder;
}

struct ProtocolHeader;
struct ProtocolLocatorHeader;
class ProtocolRegister;
enum class CacheOP : std::uint32_t;

class Framework;
using TimerCallback = std::function<void(TimerHandle, std::uint64_t userData)>;
using EntityIDVector = std::vector<std::uint64_t>;

enum class HttpResult {
    HTTP_EVENT_MESSAGE = 1,
    HTTP_EVENT_CLOSE,
    HTTP_EVENT_TIMEOUT,
    HTTP_EVENT_ERROR,
};

using HttpMethod = std::function<void(khttp::HttpRequest&, HttpResult, std::int32_t)>;
class InternalHttpCallback;

// Module framework
class ModuleFrame : public Service {
    Engine* engine_; // engine
    Framework* framework_; // logic framework
    std::unique_ptr<char> sendBuffer_; // send buffer
    // The timer Info. for lamda function
    struct TimerInfo {
        TimerCallback cb; // callback
        std::uint64_t userData; // user defined data
        bool once; // Only once
    };
    using TimerMap = UNORDERED_MAP(TimerHandle, TimerInfo, ModuleFrame_timerMap_);
    TimerMap timerMap_; // timer ID map
    using HttpServerList = std::list<InternalHttpCallback*>;
    HttpServerList httpServerList_; // HTTP server callback list
    using HttpDeadCbList = std::list<InternalHttpCallback*>;
    HttpDeadCbList httpDeadCbList_; // HTTP dead request callback list
    using LocatorMessageMethod = std::function<void(PathID, std::uint32_t, const char*, std::size_t)>;
    using LocatorMessageHandlerMap = UNORDERED_MAP(std::uint32_t, LocatorMessageMethod, ModuleFrame_locatorHandlerMap_);
    LocatorMessageHandlerMap locatorHandlerMap_; // Locator message's handler map
    using PathIDVector = std::vector<PathID>;
    using TypePathMap = UNORDERED_MAP(std::uint32_t, PathIDVector, ModuleFrame_typePathMap_);
    TypePathMap typePathMap_; // { Cache entity type, listeners' path IDs }
    ModuleType moduleType_; // Module type
    bool stopped_; // Stop flag
    bool started_; // Start flag
    bool installed_; // Install flag
    using TimerIDList = std::list<TimerHandle>;
    TimerIDList cancelledTimers_; // cancelled timer
    std::string serviceName_; // Service name

public:
    ModuleFrame();
    virtual ~ModuleFrame();
    virtual bool install(Engine* engine) override;
    virtual bool uninstall() override;
    virtual bool start(Engine* engine) override;
    virtual bool stop() override;
    virtual std::uint32_t getType() override;
    virtual void onTick(std::time_t millionseconds) override;
    virtual std::string execute(const std::string& command) override;
    virtual void onPathJoin(PathID pathID, SiteType siteType, UserData userData) override;
    virtual void onPathDisjoin(PathID pathID, SiteType siteType) override;
    virtual void onFindPathTimeout(SiteType siteType, UserData userData) override;
    virtual void onPathData(PathID pathID, SiteType siteType, const char* data,
        std::size_t size) override;
    virtual void onLocatorChange(std::uint64_t entityID, std::uint32_t entityType, PathID from, PathID to) override;
    virtual void onSessionJoin(SessionID sessionID) override;
    virtual void onSessionDisjoin(SessionID sessionID) override;
    virtual void onSessionData(SessionID sessionID, const char* data, std::size_t size) override;
    virtual void onTimer(TimerHandle timerID) override;
    virtual void onSiteJoin(const kratos::grid::SiteID& siteID) override;
    virtual void onSiteDisjoin(const kratos::grid::SiteID& siteID) override;
    virtual void onSiteData(const kratos::grid::SiteID& siteID, const char* data, std::size_t size) override;
    virtual void onLocatorJoin(PathID pathID) override;
    virtual void onLocatorDisjoin(PathID pathID) override;
    virtual bool isStarted() override;
    virtual void setServiceName(const std::string& serviceName) override;
    virtual void onHttpData(SessionID sessionID, const kratos::util::HttpProto& httpProto) override;
    virtual void onSessionConnected(SessionID sessionID, std::uint64_t userData) override;

public:
    // set framework instance
    void setFramework(Framework* framework);
    // Returns framework
    Framework* getFramework();
    // start timer periodically, wrapper for Engine::startTimer
    TimerHandle startTimer(std::time_t duration, std::uint64_t userData);
    // start timer only once, wrapper for Engine::startTimerOnce
    TimerHandle startTimerOnce(std::time_t duration, std::uint64_t userData);
    // start timer
    // @param duration timer period in millionseconds
    // @param cb timer callback
    // @param userData user defined data
    // @retval INVALID_TIME_ID fail
    // @retval timer ID
    TimerHandle startTimer(std::time_t duration, TimerCallback cb, std::uint64_t userData = 0);
    // start timer and stop it when timeout 
    // @param duration timer period in millionseconds
    // @param cb timer callback
    // @param userData user defined data
    // @retval INVALID_TIME_ID fail
    // @retval timer ID
    TimerHandle startTimerOnce(std::time_t duration, TimerCallback cb, std::uint64_t userData = 0);
    // cancel timer, wrapper for Engine::cancel
    void cancelTimer(TimerHandle timerID);
    // returns new http request
    // @param cb callback
    // @param host remote host
    // @param uri URI
    // @param port remote server port
    // @retval nullptr fail
    // @retval khttp::HttpRequest instance
    khttp::HttpRequest* newHttpRequest(khttp::HttpCallback* cb, const std::string& host, const std::string& uri, int32_t port = 80);
    // returns new http request
    // @param cb callback
    // @param host remote host
    // @param uri URI
    // @param port remote server port
    // @retval nullptr fail
    // @retval khttp::HttpRequest instance
    khttp::HttpRequest* newHttpRequest(HttpMethod cb, const std::string& host, const std::string& uri, int32_t port = 80);
    // start a new http server
    // @param cb callback
    // @param host The host listening at
    // @param port The port listening at
    // @retval true
    // @retval false fail
    bool newHttpServer(khttp::HttpCallback* cb, const std::string& host, int32_t port = 80);
    // start a new http server
    // @param cb callback
    // @param host The host listening at
    // @param port The port listening at
    // @retval true
    // @retval false fail
    bool newHttpServer(HttpMethod cb, const std::string& host, int32_t port = 80);
    // send command to specific module and wait for result
    std::string doCommand(const std::string& moduleName, const std::string& command);
    // send command to engine and wait for result
    std::string doCommand(const std::string& command);
    // returns verbose logger
    Log& getLogverb();
    // returns debug logger
    Log& getLogdebug();
    // returns warning logger
    Log& getLogwarn();
    // returns error logger
    Log& getLogerror();
    // returns fatal logger
    Log& getLogfatal();
    // returns logger finisher
    const Finish& getLogend();
    // Returns object pool manager of engine
    ObjectPoolManager& getObjectPoolManager();
    // Returns component manager
    ComponentManager& getComponentManager();
    // Returns total heap size in engine's memory pool
    std::size_t getEngineMemoryPoolSize();
    // Gets a new sequential UUID
    std::uint64_t getUUID();
    // Gets a new random UUID
    std::uint64_t getUUIDRandom();
    // Returns locator path
    PathID getLocator(std::uint64_t);

protected:
    // returns engine
    Engine* getEngine();
    // Returns service name
    const std::string& getRootPath();
    // Appends HTTP callback to dead list
    void addHttpDeadCb(InternalHttpCallback* cb);
    // Dispose all HTTP callback in dead list
    void disposeAllHttpDeadCb();
    // Deal with the message of locator related
    // @param pathID path ID
    // @param msgID message ID
    // @param data message data
    // @param size message size
    void doLocatorMessage(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Send back the ACK message
    // @param pathID path ID
    // @param msgID message ID
    // @param builder flatbuffers builder
    // @retval true
    // @retval false fail
    bool acknowledge(PathID pathID, std::uint32_t msgID, flatbuffers::FlatBufferBuilder& builder);
    // Send back the ACK message
    // @param pathIDs path ID vector
    // @param msgID message ID
    // @param builder flatbuffers builder
    // @retval true
    // @retval false fail
    bool acknowledge(const PathIDVector& pathIDs, std::uint32_t msgID, flatbuffers::FlatBufferBuilder& builder);
    // Send search message to all locator
    // @param entityType entity type
    // @param maxCount the maximum item count for each search
    // @param searchType search type
    // @retval true
    // @retval false fail
    bool searchEntity(std::uint32_t entityType, std::uint32_t maxCount, std::uint32_t searchType);
    // Build and send back the search result
    // @param pathID path ID
    // @param entityIDs search result
    // @param entityType entity type
    // @param count current pushing count
    // @param total total count
    void returnSearchResult(PathID pathID, EntityIDVector& entityIDs, std::uint32_t entityType,
        std::size_t count, std::size_t total);

private:
    // Deal with message RELAY_REQ
    void onRelayReq(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message RELAY_ACK
    void onRelayAck(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message REGISTER_REQ
    void onRegisterReq(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message REGISTER_ACK
    void onRegisterAck(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message UNREGISTER_REQ
    void onUnregisterReq(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message UNREGISTER_ACK
    void onUnregisterAck(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message SEARCH_REQ
    void onSearchReq(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message SEARCH_ACK
    void onSearchAck(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);
    // Deal with message SEARCH_NTF
    void onSearchNtf(PathID pathID, std::uint32_t msgID, const char * data, std::size_t size);

private:
    // Notify new entity to all customer which care about 'entityType'
    // @param entityType entity type
    // @param entityID entity ID
    void notifyCacheAdd(std::uint32_t entityType, std::uint64_t entityID);
    // Notify entity's leaving to all customer which care about 'entityType'
    // @param entityType entity type
    // @param entityID entity ID
    void notifyCacheRemove(std::uint32_t entityType, std::uint64_t entityID);
    // Builds protocol header
    // @param offset The offset(sendBuffer_)
    // @param msgID message ID
    // @param locatorMsgLength The message length of locator message
    // @param length The message length include header length
    ProtocolLocatorHeader* buildLocatorHeaderInSendBuffer(std::size_t offset, std::uint32_t msgID,
        std::uint32_t locatorMsgLength, std::uint32_t length);
    // Notify entity's cache operation to all customer which care about 'entityType'
    // @param entityType entity type
    // @param entityID entity ID
    // @param cacheOP cache operation
    void notifyCacheOP(std::uint32_t entityType, std::uint64_t entityID, CacheOP cacheOP);

    friend class InternalHttpCallback;
    friend class Framework;
    friend class Entity;
    friend class AddressEntity;
    friend class EntityManager;
    friend class EntitySearchCache;
    friend class EntitySearchCacheManager;
    friend class RPC;
};

#define ModuleRef (*kratos::corelib::Singleton<ModuleFrame>::instance())
#define ModuleSingleton kratos::corelib::Singleton<ModuleFrame>

#define verblog (ModuleRef.getLogverb())
#define debuglog (ModuleRef.getLogdebug())
#define warnlog (ModuleRef.getLogwarn())
#define errorlog (ModuleRef.getLogerror())
#define fatallog (ModuleRef.getLogfatal())
#define endlog (ModuleRef.getLogend())
